package com.sheepone.util.beanconvert.json;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

import com.sheepone.util.ObjectUtil;
import com.sheepone.util.ReflectUtil;
import com.sheepone.util.ArrayUtil;
import com.sheepone.util.anno.Alias;
import com.sheepone.util.anno.Regex;
import com.sheepone.util.anno.StartWith;
import com.sheepone.util.anno.ZeroIfNaN;
import com.sheepone.util.exception.Assert;
import com.sheepone.util.string.StringUtil;
import org.apache.commons.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author Miss.杨
 * @description 将JSON字符串转换成JavaBean对象
 * @see Alias
 * @see Regex
 * @see StartWith
 * @see ZeroIfNaN
 * @since 2024/2/20 - 21:58
 */
public class JsonBeanConvertUtil {

    private static final Logger logger = LoggerFactory.getLogger(new Object() {
    }.getClass().getEnclosingClass());

    public static void fillArray(String jsonStr, Object[] objectArray) {
        fillArray(jsonStr, objectArray, true);
    }


    public static void fillArray(String jsonStr, Object[] objectArray, boolean enableHumpMapping) {
        Assert.notNull(jsonStr, "jsonStr cannot be null");
        Assert.notEmpty(objectArray, "objectArray cannot be empty");
        JSONArray jsonArray = JSON.parseArray(jsonStr);
        JsonFieldCovertMapping mapping = new JsonFieldCovertMapping(jsonArray, objectArray, enableHumpMapping);
        for (int i = 0; i < jsonArray.size(); i++) {
            fillByJSONObject(jsonArray.getJSONObject(i), objectArray[i], mapping);
        }
    }

    public static void fillObject(String jsonStr, Object objectArray) {
        fillObject(jsonStr, objectArray, true);
    }


    public static void fillObject(String jsonStr, Object object, boolean enableHumpMapping) {
        Assert.notNull(jsonStr, "jsonStr cannot be null");
        Assert.notNull(object, "object cannot be empty");
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        JsonFieldCovertMapping mapping = new JsonFieldCovertMapping(jsonObject, object, enableHumpMapping);
        fillByJSONObject(jsonObject, object, mapping);
    }

    private static void fillByJSONObject(JSONObject jsonObject, Object object, JsonFieldCovertMapping mapping) {
        Field[] fields = ReflectUtil.accessibleDeclaredFields(object.getClass());
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            String fieldName = field.getName();
            List<String> mappingNames = mapping.mappingValues(fieldName);
            if (mappingNames != null && !mappingNames.isEmpty()) {
                for (int j = 0; j < mappingNames.size(); j++) {
                    String mappingName = mappingNames.get(i);
                    Class<?> type = field.getType();
                    if (ObjectUtil.isBasicType(type)) {
                        ReflectUtil.setValue(field, object, jsonObject.getObject(mappingName, type));
                    } else {
                        // todo 待测试
                        Object value = ReflectUtil.getValue(field, object);
                        JSONObject subObject = jsonObject.getJSONObject(mappingName);
                        fillByJSONObject(subObject, value, new JsonFieldCovertMapping(subObject, value, mapping.isEnableHumpMapping()));
                    }
                }
            }
        }

    }

    /**
     * 处理Json数组的解析，默认开启驼峰映射
     *
     * @param jsonStr 待处理的json字符串
     * @param clazz   待处理的结果类
     * @param <T>     泛型参数
     * @return 返回处理结果后的目标类
     * @throws Exception
     */
    public static <T> T[] convertArray(String jsonStr, Class<T> clazz) {
        return convertArray(jsonStr, clazz, true);
    }


    /**
     * 处理Json数组的解析，根据enableHumpMapping是否开启驼峰映射
     *
     * @param jsonStr           待处理的json字符串
     * @param clazz             待处理的结果类
     * @param enableHumpMapping 是否开启驼峰映射
     * @param <T>               泛型参数
     * @return 返回处理结果后的目标类
     * @throws Exception
     */
    public static <T> T[] convertArray(String jsonStr, Class<T> clazz, boolean enableHumpMapping) {
        if (jsonStr == null) {
            throw new IllegalArgumentException("jsonStr cannot be null");
        }
        JSONArray jsonArray = JSON.parseArray(jsonStr);
        if (jsonArray == null) {
            throw new IllegalArgumentException("JSONArray cannot be null");
        }
        Objects.requireNonNull(clazz, "clazz cannot be null");
        T[] objects = ArrayUtil.newArray(clazz, jsonArray.size());
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            try {
                objects[i] = handleJSONObject(jsonObject, clazz, enableHumpMapping);
            } catch (Exception e) {
                if (logger.isErrorEnabled()) {
                    logger.error("Json转换异常：{}", e.getMessage());

                }
            }
        }
        return objects;
    }

    /**
     * 处理Json对象的解析，默认开启驼峰映射
     *
     * @param jsonStr 待处理的json字符串
     * @param clazz   待处理的结果类
     * @param <T>     泛型参数
     * @return 返回处理结果后的目标类
     * @throws Exception
     */
    public static <T> T convertObject(String jsonStr, Class<T> clazz) throws Exception {
        return convertObject(jsonStr, clazz, true);
    }

    /**
     * 处理Json对象的解析，根据enableHumpMapping是否开启驼峰映射
     *
     * @param jsonStr           待处理的json字符串
     * @param clazz             待处理的结果类
     * @param enableHumpMapping 是否开启驼峰映射
     * @param <T>               泛型参数
     * @return 返回处理结果后的目标类
     * @throws Exception
     */
    public static <T> T convertObject(String jsonStr, Class<T> clazz, boolean enableHumpMapping) throws Exception {
        if (ObjectUtil.isBasicType(clazz)) {
            return ReflectUtil.writeStringToBasicObject(jsonStr, clazz);
        }
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        return handleJSONObject(jsonObject, clazz, enableHumpMapping);
    }

    /**
     * 处理fastjson工具包的JSONObject对象的解析，根据enableHumpMapping是否开启驼峰映射
     *
     * @param jsonObject        待处理的json对象
     * @param clazz             待处理的结果类
     * @param enableHumpMapping 是否开启驼峰映射
     * @param <T>               泛型参数
     * @return 返回处理结果后的目标类
     * @throws Exception
     */
    private static <T> T handleJSONObject(JSONObject jsonObject, Class<T> clazz, boolean enableHumpMapping) throws Exception {
        Constructor<T> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        T instance = constructor.newInstance();

        Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            // 初始化结果集集合
            List<String> valueStrs = new ArrayList<>();

            // 处理 @StartWith
            processStartWith(field, entries, valueStrs);

            // 处理 @Regex
            processRegex(field, entries, valueStrs);

            // 先处理可能是复合类型的相关注解，再处理只是单一类型的注解
            // 处理 @Alias
            processAlias(field, entries, valueStrs);

            // 直接根据名称匹配
            if (valueStrs.isEmpty()) {
                processOriginalName(field, entries, valueStrs);
            }

            // 处理驼峰映射
            if (valueStrs.isEmpty() && enableHumpMapping) {
                processHumpName(field, entries, valueStrs);
            }

            // 处理结果集
            handleValueStrings(field, instance, valueStrs);
        }

        return instance;
    }

    /**
     * 处理驼峰映射
     *
     * @param field     目标字段
     * @param entries   目标集合
     * @param valueStrs 结果集
     */
    private static void processHumpName(Field field, Set<Map.Entry<String, Object>> entries, List<String> valueStrs) {
        String name = field.getName();
        for (Map.Entry<String, Object> entry : entries) {
            String underscoreToCamelCase = StringUtil.underscoreToCamelCase(entry.getKey());
            if (name.equals(underscoreToCamelCase)) {
                valueStrs.add(entry.getValue().toString());
                // 匹配一个即可
                break;
            }
        }
    }

    /**
     * 直接根据名称匹配
     *
     * @param field     目标字段
     * @param entries   目标集合
     * @param valueStrs 结果集
     */
    private static void processOriginalName(Field field, Set<Map.Entry<String, Object>> entries, List<String> valueStrs) {
        if (valueStrs.isEmpty()) {
            String name = field.getName();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                if (key.equals(name)) {
                    valueStrs.add(entry.getValue().toString());
                    // 匹配一个即可
                    break;
                }
            }
        }
    }

    /**
     * 处理 @Alias 注解
     *
     * @param field
     * @param entries
     * @param valueStrs
     * @throws Exception
     */

    private static void processAlias(Field field, Set<Map.Entry<String, Object>> entries, List<String> valueStrs) {
        Alias alias = field.getAnnotation(Alias.class);
        if (alias == null) {
            return;
        }

        String[] aliasStrs = alias.value();
        for (Map.Entry<String, Object> entry : entries) {
            for (String aliasStr : aliasStrs) {
                if (aliasStr.equals(entry.getKey())) {
                    valueStrs.add(entry.getValue().toString());
                    // 匹配一个即可
                    return;
                }
            }
        }
    }

    /**
     * 处理 @Regex 注解
     *
     * @param field
     * @param entries
     * @param valueStrs
     * @param <T>
     * @throws Exception
     */
    private static <T> void processRegex(Field field, Set<Map.Entry<String, Object>> entries, List<String> valueStrs) {
        Regex regex = field.getAnnotation(Regex.class);
        if (regex == null) {
            return;
        }

        String regexStr = regex.value();
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            if (key.matches(regexStr)) {
                valueStrs.add(entry.getValue().toString());
            }
        }
    }

    /**
     * 处理 @StartWith 注解
     *
     * @param field
     * @param entries
     * @param valueStrs
     * @param <T>
     * @throws Exception
     */
    private static <T> void processStartWith(Field field, Set<Map.Entry<String, Object>> entries, List<String> valueStrs) {
        StartWith startWith = field.getAnnotation(StartWith.class);
        if (startWith == null) {
            return;
        }
        String[] prefixes = startWith.value();
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            for (String prefix : prefixes) {
                if (key.startsWith(prefix)) {
                    valueStrs.add(entry.getValue().toString());
                }
            }
        }
    }

    /**
     * 结果集处理
     *
     * @param field
     * @param instance
     * @param valueStrs
     * @param <T>
     * @throws Exception
     */
    private static <T> void handleValueStrings(Field field, T instance, List<String> valueStrs) throws Exception {
        if (valueStrs.isEmpty()) {
            return;
        }
        field.setAccessible(true);
        Class<?> fieldType = field.getType();
        // 处理单一类型
        if (ObjectUtil.isSingleType(fieldType)) {
            String string = valueStrs.get(0);
            // todo 后续拆分为
            // 加工
            // 过滤
            // 选择
            // 写入
            if (ObjectUtil.isBasicType(fieldType)) {

                string = processZeroIfNaN(field, string);

                ReflectUtil.writeStringToBasicField(field, instance, string, fieldType);
            } else {
                field.set(instance, convertObject(string, fieldType));
            }
        } else {
            // 处理数组类型
            if (fieldType.isArray()) {
                Class<?> type = fieldType.getComponentType();
                if (ObjectUtil.isBasicType(type)) {
                    ReflectUtil.writeStringsToBasicArray(field, instance, valueStrs, type);
                } else {
                    Object[] objects = new Object[valueStrs.size()];
                    for (int i = 0; i < objects.length; i++) {
                        objects[i] = convertObject(valueStrs.get(i), type);
                    }
                    field.set(instance, objects);
                }
            } else if (ClassUtils.isAssignable(fieldType, List.class)) {
                // todo
            } else if (ClassUtils.isAssignable(fieldType, Map.class)) {
                // todo
            } else if (ClassUtils.isAssignable(fieldType, Set.class)) {
                // todo
            } else {
                throw new ClassNotFoundException("field type is not support");
            }
        }
    }

    /**
     * 处理 @ZeroIfNaN 注解
     *
     * @param field
     * @param string
     * @return
     */
    private static String processZeroIfNaN(Field field, String string) {
        ZeroIfNaN zeroIfNaN = field.getAnnotation(ZeroIfNaN.class);
        if (zeroIfNaN != null && String.valueOf(Double.NaN).equals(string)) {
            string = "0.00";
        }
        return string;
    }


}